package xwebsocket

import (
	"context"
	"net/http"
	"runtime"
	"sync"
	"sync/atomic"
	"time"

	"gitee.com/lihaiping1603/utils"

	"gitee.com/lihaiping1603/utils/log"
	"gitee.com/lihaiping1603/utils/uniq"
	"github.com/gorilla/websocket"
)

var smgr = &sessionMgr{sessions: make(map[string]*WebsocketSession)}

type sessionMgr struct {
	mutex    sync.Mutex
	sessions map[string]*WebsocketSession
}

func (smgr *sessionMgr) newWebsocketSession(conn *websocket.Conn, handler WebsocketHandler) *WebsocketSession {
	id := uniq.UUID()
	smgr.mutex.Lock()
	defer smgr.mutex.Unlock()
	for {
		if _, ok := smgr.sessions[id]; ok {
			id = uniq.UUID()
			continue
		}
		break
	}
	session := &WebsocketSession{Conn: conn, id: id, handler: handler, mgr: smgr, rclose: make(chan struct{})}
	smgr.sessions[id] = session
	return session
}

func (smgr *sessionMgr) onClosed(session *WebsocketSession) error {
	smgr.mutex.Lock()
	defer smgr.mutex.Unlock()
	delete(smgr.sessions, session.ID())
	return nil
}

//panic: concurrent write to websocket connection
//https://pkg.go.dev/github.com/gorilla/websocket#hdr-Concurrency
//websocket 并发读,或者并发写,会出现安全的问题,所以需要加locker
type WebsocketSession struct {
	*websocket.Conn
	mutex   sync.Mutex
	handler WebsocketHandler
	id      string
	mgr     *sessionMgr
	header  http.Header
	uri     string
	close   int32         //
	rclose  chan struct{} //
}

func (s *WebsocketSession) ID() string {
	return s.id
}

func (s *WebsocketSession) GetHandshakeHeaders() http.Header {
	return s.header
}

func (s *WebsocketSession) GetUri() string {
	return s.uri
}

//该函数主动关闭会话,在关闭链接之前,会先发送一个closemessage消息给对端，然后才关闭链接
func (s *WebsocketSession) ShutDown(ctx context.Context) error {
	//如果已经关闭了
	if atomic.LoadInt32(&s.close) > 0 {
		return nil
	}
	writeWait := 10 * time.Second
	//最后关闭连接
	defer s.Close()
	//先给对端发送关闭消息，然后
	if err := s.Conn.SetWriteDeadline(time.Now().Add(writeWait)); err != nil {
		log.Error("websocketsession:%s err:%s", s.id, err.Error())
		return err
	}
	if err := s.Conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "shutdown")); err != nil {
		log.Error("websocketsession:%s write close message to peer err:%s", s.id, err.Error())
		return err
	}

	select {
	case <-ctx.Done():
	case <-s.rclose:
	}
	return nil
}

//close 函数是直接关闭底层的socket链接,如果消息存在未读取和未发送的情况，就会丢失，属于暴力关闭链接的方式
func (s *WebsocketSession) Close() error {
	if atomic.LoadInt32(&s.close) > 0 {
		return nil
	}
	atomic.AddInt32(&s.close, 1)
	if err := s.Conn.Close(); err != nil {
		log.Error("close websocketsession:%s err:%s", s.id, err.Error())
	}
	if s.handler != nil {
		s.handler.OnClosed(s)
	}
	s.mgr.onClosed(s)
	close(s.rclose)
	return nil
}

func (s *WebsocketSession) readCycle() {
	defer func() {
		if err := recover(); err != nil {
			switch err.(type) {
			case runtime.Error: // 运行时错误
				log.Error("runtime error: %s", err)
			default: // 非运行时错误
				log.Error("error: %s", err)
			}
			utils.PanicTrace()
		}
	}()
	defer func() {
		s.Close()
	}()
	for {
		messageType, message, err := s.ReadMessage()
		if err != nil {
			log.Error("session[%s] readmessage err:%s", s.ID(), err.Error())
			break
		}
		switch messageType {
		case websocket.TextMessage:
			if s.handler != nil {
				s.handler.OnHandleTextMessage(s, message)
			}
		case websocket.BinaryMessage:
			if s.handler != nil {
				s.handler.OnHandleBinaryMessage(s, message)
			}
		default:
			log.Error("session[%s] readmessage type:%d message:%s err: don't support", s.ID(), messageType, message)
		}
	}
	return
}

func (s *WebsocketSession) WriteTextMessage(data []byte) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.WriteMessage(websocket.TextMessage, data)
}

func (s *WebsocketSession) WriteBinaryMessage(data []byte) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.WriteMessage(websocket.BinaryMessage, data)
}
